#include <LOGR_users.h>
#include <LOGR_gen_pages.h>
#include <LOGR_gen_info.h>
#include <LOGR_sites.h>
#include <LOGR_browsers.h>
#include <LOGR_create_webgraph.h>

#include "llist_convert.h"
#include "server_protocol.h"
#include "perror_exit.h"
#include "func_lexer.h"
#include "w3ld.h"
#include "watch.h"

extern global_frame frame;
extern pthread_mutex_t mutex;

W3L_FUNCTION(help)
{
	w3l_cmd_entry *cmd = NULL;

	if(w3l_arity == 0) {
		return construct_return(
				W3LD_build_list(list_functions(),(W3LD_cmd_ret_cvt)),
				DATA_TYPE_LIST,
				0,
				FREE_DATA);
	}

	else if(w3l_arity == 1) {
		if(!(cmd = lexer_fetch(w3l_args[0])))
			return W3L_FUNC_ERR("No Such Function");
		return W3LD_cmd_ret_cvt(cmd);
	}
	
	return W3L_FUNC_ERR(W3L_ERR_ARITY);
}

W3L_FUNCTION(shutdown)
{
	/* shut down log watching */
	/* shut down network connection */
	pthread_mutex_unlock(&mutex);
	exit(EXIT_SUCCESS);
	return NULL;
}

W3L_FUNCTION(disconnect)
{
	/* send a goodbye reply */
	/* shut off network connection */
	pthread_mutex_unlock(&mutex);
#ifdef DEBUG
	fprintf(stderr,"debug: disconnecting a client\n");
#endif
	pthread_exit(NULL);
	return NULL;
}

W3L_FUNCTION(list_users)
{
	LNode_type *ln = NULL;

	if(w3l_arity != 0)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	ln = W3LD_build_HTList(frame.webstat->lcusers,(W3LD_web_user_ptr_cvt));
	assert(ln != NULL);
	return construct_return(ln,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_sites)
{
	LNode_type *ln = NULL;

	if(w3l_arity != 0)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	// LOGR_process_sites(frame.webstat,1); // This would just resolve address, this would take way too long

	ln = W3LD_build_list(LOGR_get_list_sites(frame.webstat),(W3LD_web_site_node_ptr_cvt));
	assert(ln != NULL);
	return construct_return(ln,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_ua) {
	LNode_type *lst = NULL;
	lst = W3LD_build_list(LOGR_get_list_ua(frame.webstat),(W3LD_browser_ua_cvt));
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_browser) {
	LNode_type *lst = NULL;
	lst = W3LD_build_list(LOGR_get_list_ua(frame.webstat),(W3LD_browser_cvt));
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_platform) {
	LNode_type *lst = NULL;
	lst = W3LD_build_list(LOGR_get_list_ua(frame.webstat),(W3LD_browser_platform_cvt));
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_ext_ref) {
	LNode_type *lst = NULL;
	lst = W3LD_build_list(LOGR_get_list_url_external_referers(frame.webstat),(W3LD_gen_page_cvt));
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_int_ref) {
	LNode_type *lst = NULL;
	lst = W3LD_build_list(LOGR_get_list_url_internal_referers(frame.webstat),(W3LD_gen_page_cvt));
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_entry) {
	LNode_type *lst = NULL;
	lst = W3LD_build_list(LOGR_get_list_entry_pages(frame.webstat),(W3LD_gen_page_cvt));
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(list_domains)
{
	LNode_type *ln = NULL;

	LOGR_process_domains(frame.webstat);

	ln = W3LD_build_list(LOGR_get_list_domains(frame.webstat),(W3LD_gen_info_ptr_cvt));
	assert(ln != NULL);

	return construct_return(ln,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(base_webhost)
{
	char *webhost = (char *)LOGR_get_base_webhost(frame.webstat);

	if(w3l_arity != 0)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	assert(webhost != NULL);

	return construct_return(webhost,DATA_TYPE_STRING,strlen(webhost),DO_NOT_FREE_DATA);
}

W3L_FUNCTION(total_accesses)
{
	int *nacc = 0;
	
	if(w3l_arity != 0)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	
	MALLOCATE(nacc,int *,sizeof(int))
	*nacc = (int)LOGR_get_total_site_naccesses(frame.webstat);
	return construct_return(nacc,DATA_TYPE_INT,sizeof(int),FREE_DATA);
}

W3L_FUNCTION(list_spiders) {
	LNode_type *lst = NULL;
	lst = W3LD_build_list(LOGR_get_list_spider(frame.webstat),(W3LD_browser_ua_cvt));
	assert(lst != NULL);
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(user_accesses)
{
	WebUserPtr wup = NULL;
	int *naccesses;
	
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
			
	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);
	
	MALLOCATE(naccesses,int *,sizeof(int))
	*naccesses = (int)LOGR_get_user_naccesses(wup);
	return construct_return(naccesses,DATA_TYPE_INT,sizeof(int),FREE_DATA);
}

int path_length(WebUserPtr wup) {
	WebPathPtr wpp = NULL;
	LNode_type *ptr = NULL, *ledges = NULL;
	int path_length = 0;

	if(!wup)
		return 0;
	
	if(!(wpp = LOGR_get_user_current_path(wup)))
		return 0;
	
	ledges = LOGR_get_path_ledges(wpp);
	for(ptr=ledges->back;ptr!=ledges;ptr=ptr->back)
		++path_length;
	
	return path_length;
}

W3L_FUNCTION(user_path_length)
{
	WebUserPtr wup = NULL;
	int *path_len = NULL;

	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	MALLOCATE(path_len,int *,sizeof(int));
	*path_len = path_length(wup);

	return construct_return(path_len,DATA_TYPE_INT,sizeof(int),FREE_DATA);	
}

W3L_FUNCTION(user_path) {
	WebUserPtr wup = NULL;
	LNode_type *list;
	WebPathPtr wpp = NULL;

	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	if(!(wpp = LOGR_get_user_current_path(wup)))
		return W3L_FUNC_ERR("Could not find user's path");

	list = W3LD_build_list(LOGR_get_path_ledges(wpp),(W3LD_path_cvt));

	return construct_return(list,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(avg_path_length)
{
	HTList *lcusers = frame.webstat->lcusers;
	HTList *cur = NULL;
	void *ptr = NULL;
	int nusers = 0;
	int length = 0;
	char ret[32];

	if(!lcusers)
		return W3L_FUNC_ERR("Error Loading Users");

	cur = lcusers;
	while((ptr = HTList_nextObject(cur))) {
		++nusers;
		length += path_length((WebUserPtr)ptr);
	}
	
	printf("len: %d\tnusers: %d\n",length,nusers);

	if(!nusers)
		snprintf(ret,32,"%e",0.0);
	else
		snprintf(ret,32,"%e",(double)((double)length/(double)nusers));

	return construct_return(strdup(ret),DATA_TYPE_STRING,strlen(ret),FREE_DATA);
	
}

W3L_FUNCTION(num_active_users)
{
	HTList *cur = NULL;
	void *ptr = NULL;
	int *ncusers = NULL;
	
	MALLOCATE(ncusers,int *,sizeof(int));
	*ncusers = 0;
	
	cur = frame.webstat->lcusers;
	while((ptr = HTList_nextObject(cur)))
			++(*ncusers);
	
	return construct_return(ncusers,DATA_TYPE_INT,sizeof(int),FREE_DATA);
}

W3L_FUNCTION(user_entry_page) {
	WebUserPtr wup = NULL;
	char *entry_page = NULL;

	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	if(!(entry_page=LOGR_get_user_entry_page(wup)))
		return W3L_FUNC_ERR(W3L_ERR_NULL);

	return construct_return(strdup(entry_page),DATA_TYPE_STRING,strlen(entry_page),FREE_DATA);

}

W3L_FUNCTION(user_referer) {
	WebUserPtr wup = NULL;
	char *referer = NULL;

	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	if(!(referer=LOGR_get_user_referer(wup)))
		return W3L_FUNC_ERR(W3L_ERR_NULL);

	return construct_return(strdup(referer),DATA_TYPE_STRING,strlen(referer),FREE_DATA);
}

W3L_FUNCTION(user_start_time) {
	WebUserPtr wup = NULL;
	time_t start_time;
	char *time_str = NULL;
	int time_str_len = 0;

	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	if(!(start_time=LOGR_get_user_start_time(wup)))
		return W3L_FUNC_ERR(W3L_ERR_NULL);

	if(!(time_str=ctime(&start_time)))
		return W3L_FUNC_ERR(W3L_ERR_NULL);

	{	// chop off newline character (saving strlen)
		time_str_len = strlen(time_str) - 1;
		if(time_str_len > 1)
			time_str[time_str_len] = '\0';
	}

	return construct_return(strdup(time_str),DATA_TYPE_STRING,time_str_len,FREE_DATA);

}
W3L_FUNCTION(user_end_time) {
	WebUserPtr wup = NULL;
	time_t end_time;
	char *time_str = NULL;
	int time_str_len = 0;

	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	if(!(end_time=LOGR_get_user_end_time(wup)))
		return W3L_FUNC_ERR(W3L_ERR_NULL);

	if(!(time_str=ctime(&end_time)))
		return W3L_FUNC_ERR(W3L_ERR_NULL);

	{	// chop off newline character (saving strlen)
		time_str_len = strlen(time_str) - 1;
		if(time_str_len > 1)
			time_str[time_str_len] = '\0';
	}

	return construct_return(strdup(time_str),DATA_TYPE_STRING,time_str_len,FREE_DATA);
}

W3L_FUNCTION(user_avg_time_doc) {
	WebUserPtr wup = NULL;
	
	LNode_type *times, *ptr;
	int i;
	long sum = 0;
	long last = 0;
	int *avg = NULL;
	
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	
	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);
	
	if(!(times = LOGR_get_user_ltimes(wup)))
		return W3L_FUNC_ERR(W3L_ERR_NULL);
	
	for(ptr = times->back;ptr!=times;ptr=ptr->back) {
		if(!(i++))
			sum += (long)(ptr->info) - last;
		last = (long)(ptr->info);
	}
	
	if(!(avg = (int *)malloc(sizeof(int))))
		perror_exit("%s:%d ",__FILE__,__LINE__);
	
	*avg = sum/i;
	
	return construct_return(avg,DATA_TYPE_INT,sizeof(int),FREE_DATA);
}

W3L_FUNCTION(user_current_doc) {
	WebUserPtr wup = NULL;
	WebPathPtr wpp = NULL;
	LNode_type *ptr = NULL, *ledges = NULL;
	PEDGE e;

	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	
	if(!(wup = find_user_in_list_current_users(frame.webstat->lcusers, w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	if(!(wpp = LOGR_get_user_current_path(wup)))
		return W3L_FUNC_ERR(W3L_ERR_USR_NOT_FOUND);

	if(wpp->ledges->next) {
		PEDGE e = (PEDGE)(wpp->ledges->next);
		PNODE n = get_to_vertex(e);
		char *label = get_label_vertex(n);
		return construct_return(strdup(label),DATA_TYPE_STRING,strlen(label),FREE_DATA);
	}
	
	return W3L_FUNC_ERR(W3L_ERR_NULL);
}


W3L_FUNCTION(version)
{
	return construct_return(strdup(PACKAGE_STRING),DATA_TYPE_STRING,strlen(PACKAGE_STRING),FREE_DATA);
}


W3L_FUNCTION(watch_url)
{
	w3lWatch *watch = NULL;
	int *id;
	
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	
	if(!(id = (int *)malloc(sizeof(int))))
		perror_exit("malloc() (%s:%d)",__FILE__,__LINE__);
	
	*id = max_watch_id(frame.watches)+1;

	watch = create_watch("URL Watch",1,*id,(void *)w3l_args[0],1,(w_url));
	add_watch(frame.watches,watch);
	
	return construct_return(id,DATA_TYPE_INT,sizeof(int),FREE_DATA);
}

W3L_FUNCTION(watch_user)
{
	w3lWatch *watch = NULL;
	int *id;
	
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	
	if(!(id = (int *)malloc(sizeof(int))))
		perror_exit("malloc() (%s:%d)",__FILE__,__LINE__);
	
	*id = max_watch_id(frame.watches)+1;

	watch = create_watch("User Watch",1,*id,w3l_args[0],1,(w_domain));
	add_watch(frame.watches,watch);
	
	return construct_return(id,DATA_TYPE_INT,sizeof(int),FREE_DATA);
}

W3L_FUNCTION(watch_worm)
{
	w3lWatch *watch = NULL;
	int *id;
	
	if(w3l_arity != 0)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	
	return W3L_FUNC_ERR(W3L_ERR_NOT_IMPLEMENTED);
/*	
	if(!(id = (int *)malloc(sizeof(int))))
		perror_exit("malloc() (%s:%d)",__FILE__,__LINE__);
	
	*id = max_watch_id(frame.watches)+1;

	add_watch(frame.watches,watch);
	
	return construct_return(id,DATA_TYPE_INT,sizeof(int),FREE_DATA);
*/
}

W3L_FUNCTION(unwatch)
{
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	
	if(del_watch(frame.watches,atoi(w3l_args[0])))
		return W3L_FUNC_ERR(W3L_ERR_OK);
	
	return W3L_FUNC_ERR("No Such Watch");
}

W3L_FUNCTION(check_watch)
{	
	char *status = NULL;
	w3lWatch *watch = NULL;
	
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if(!(watch = find_watch(frame.watches,atoi(w3l_args[0]))))
		return W3L_FUNC_ERR("No Such Watch");
		
	if(!(status = check_watch(watch)))
		return W3L_FUNC_ERR("");
	
	return construct_return(status,DATA_TYPE_STRING,sizeof(status),FREE_DATA);
}

W3L_FUNCTION(watches)
{
	LNode_type *list;
	list = W3LD_build_list(frame.watches,(watch_convert));
	assert(list != NULL);
	return construct_return(list,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

GraphFrame *make_graph() {
	GraphFrame *ugr = NULL;
	ugr = create_current_graph();
	set_dimensions(ugr, GRAPH_WIDTH, GRAPH_HEIGHT);
	set_show_vertex_weight(ugr, False);
	set_show_edge_weight(ugr, False);
	set_shape(ugr, BOX);
	set_dim_vertex(ugr, BSIDE, HSIDE);
	set_scale(ugr, 0);
	set_flag_draw(ugr, 0);
	set_num_vertex(ugr, 0);
	set_num_ver_group(ugr, 0);	
	set_count_vertex(ugr, 0);
	set_method_tree(ugr, BFS_TREE);
	set_graph_dir(ugr, DIRECTED);
	return ugr;
}

W3L_FUNCTION(webgraph)
{
	move_graph_to_given_position(frame.graph,0,0);
	return construct_return(frame.graph,DATA_TYPE_GRAPH,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(user_webgraph)
{

	GraphFrame *ugr = make_graph();

	LOGR_create_webgraph_from_user_graphs(frame.webstat, frame.graph, ugr);

	compute_random_positions(ugr);
	compute_force_directed_positions(ugr);
	set_scale(ugr,-6);

	return construct_return(ugr,DATA_TYPE_GRAPH,0,DO_NOT_FREE_DATA); // how do i delete a graph?

}

W3L_FUNCTION(links_webgraph)
{
	GraphFrame *ugr = make_graph();

	LOGR_create_webgraph_from_logs_links(frame.webstat, frame.graph, ugr);

	compute_random_positions(ugr);
	compute_force_directed_positions(ugr);
	set_scale(ugr,-6);

	return construct_return(ugr,DATA_TYPE_GRAPH,0,DO_NOT_FREE_DATA); // how do i delete a graph?
}

W3L_FUNCTION(graph_size) {
	unsigned int x, y;
	int *ret = NULL;
	if(w3l_arity != 2)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	x = (unsigned int)atoi(w3l_args[0]);
	y = (unsigned int)atoi(w3l_args[1]);
	if(x > 5000 || y > 5000)
		return W3L_FUNC_ERR("I think that size is too large");
	set_dimensions(frame.graph,x,y);
	
	MALLOCATE(ret,int *,sizeof(int));
	*ret = 1;
	return construct_return(ret,DATA_TYPE_INT,0,FREE_DATA);
}

W3L_FUNCTION(graph_scale) {
	int s;
	int *ret;
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);
	s = atoi(w3l_args[0]);
	if(s < -7 || s > 20)
		return W3L_FUNC_ERR("Scale out of bounds");
	set_scale(frame.graph,s);
	MALLOCATE(ret,int *,sizeof(int));
	*ret = 1;
	return construct_return(ret,DATA_TYPE_INT,0,FREE_DATA);
}

struct gr_algo {
	char *name;
	int (*algo)(GraphFrame *gr);
};

const struct gr_algo algos[] = {
	{"force_directed", (compute_force_directed_positions)},
	{"random", (compute_random_positions)},
	{"circular", (compute_circular_positions)},
	{"horizontal", (compute_horizontal_positions)},
	{"diagonal", (compute_horizontal_positions)},
	{"linear", (compute_linear_positions)},
	{"layered_pretty", (compute_layered_pretty_positions)},
	{"radial_pretty", (compute_radial_pretty_positions)},
	{"levels",(compute_level_positions)},
	{NULL,NULL}
	};
	
unsigned short cnt_algos(void) {
	short i = 0;
	while(algos[i].name != NULL)
		++i;
	return i;
}

W3L_FUNCTION(list_graph_algos) {
	short i = 0;
	LNode_type *str = NULL;
	LNode_type *lst = NULL;

	str = init_linked_list();
	while(algos[i].name != NULL)
		Insert_linked_list(str,create_new_node_llist(algos[i++].name));

	lst = W3LD_build_list(str,(W3LD_string_cvt));
	assert(lst != NULL);
	return construct_return(lst,DATA_TYPE_LIST,0,DO_NOT_FREE_DATA);
}

W3L_FUNCTION(graph_algo) {
	unsigned short algo;
	int *ret;
	if(w3l_arity != 1)
		return W3L_FUNC_ERR(W3L_ERR_ARITY);

	if((algo = (unsigned short)atoi(w3l_args[0]))>=cnt_algos())
		return W3L_FUNC_ERR("No Such Algorithm");

	if((algos[algo].algo)(frame.graph))
		return W3L_FUNC_ERR("Failed to redraw graph");

	MALLOCATE(ret,int *,sizeof(int));
	*ret = 1;

	return construct_return(ret,DATA_TYPE_INT,0,FREE_DATA);

}


void load_user_functions(void) {

	frame.froot = init_func_tree_node();

	/* Internal Commands */
	W3L_COMMAND(help,"Get help information on a given command",1,"Name of a function");
	W3L_COMMAND(shutdown,"Shut down the w3live server",0);
	W3L_COMMAND(disconnect,"Disconnect from the server",0);

	/* Server Commands */
	W3L_COMMAND(base_webhost,"Get the base webhost setting",0);
	W3L_COMMAND(total_accesses,"The total number of accesses to the site",0);

	/* User Stuff */
	W3L_COMMAND(user_accesses,"The number of requests from a given user",1,"user name");
	W3L_COMMAND(user_entry_page,"First document requested from a given user",1,"user name");
	W3L_COMMAND(user_referer,"Referer for a given user",1,"user name");
	W3L_COMMAND(user_start_time,"Time at which user first accessed the site",1,"user name");
	W3L_COMMAND(user_end_time,"Time at which user last accessed the site",1,"user name");
	W3L_COMMAND(user_avg_time_doc,"Average time spent per document by a given user",1,"user name");
	W3L_COMMAND(user_current_doc,"The last document accessed by a given user",1,"user name");
	W3L_COMMAND(user_path,"A users path info with tab delimited URL, MIME, http code",1,"user name");

	W3L_COMMAND(user_path_length,"Length of the given user's path through the site",1,"user name");
	W3L_COMMAND(avg_path_length,"Average length of all active user's paths",0);
	W3L_COMMAND(num_active_users,"Number of currently active users",0);
	
	/* Watch Stuff */
	W3L_COMMAND(check_watch,"Check the status of a watch",1,"Watch ID");
	W3L_COMMAND(unwatch,"Stop a watch",1,"Watch ID");
	W3L_COMMAND(watch_user,"Watch for when a given domain string matches a web user",1,"Domain");
	W3L_COMMAND(watch_url,"Watch for when a given URL is accessed",1,"URL");
	W3L_COMMAND(watch_worm,"Watch for if any worms are suspected to be probing your server",0);
	W3L_COMMAND(watches,"List all current watches",0);
	
	/* Other things */
	W3L_COMMAND(version,"Current w3live version",0);

	/* Graph Stuff */
	W3L_COMMAND(webgraph,"Current site graph",0);
	W3L_COMMAND(user_webgraph,"Graph of current users",0);
	W3L_COMMAND(links_webgraph,"Graph of links from log",0);


	/* Graph Manipulation */
	W3L_COMMAND(graph_size,"Set Graph Size",2,"width","height");
	W3L_COMMAND(graph_scale,"Set Graph Scale",1,"integer between -7 and 20 inclusive");
	W3L_COMMAND(graph_algo,"Run a graph drawing algorithm",1,"algorithm name");
	W3L_COMMAND(list_graph_algos,"List all available graph drawing algorithms",0);
	
	/* List Stuff */
	W3L_COMMAND(list_users,"Get a list of current users (those active on the site)",0);
	W3L_COMMAND(list_sites,"Get a list of all active website users",0);
	W3L_COMMAND(list_domains,"Get a list of all active domains",0);
	W3L_COMMAND(list_spiders,"Get a list of spiders who have accessed your site",0);
	W3L_COMMAND(list_ua,"List of User Agents",0);
	W3L_COMMAND(list_browser,"List of Browsers",0);
	W3L_COMMAND(list_platform,"List of Computer Platforms",0);
	W3L_COMMAND(list_ext_ref,"List of external referers",0);
	W3L_COMMAND(list_int_ref,"List of internal referers",0);
	W3L_COMMAND(list_entry,"List of site entry pages",0);
	//W3L_COMMAND(list_http_code,"List all requested documents which returned code 404",1,"http code (i.e. 404)");
}
